/**
 * This file is part of the CAKIS project.
 * 
 * Copyright (c) 2009-2010 - Florian Arndt, Christian Kerl
 * 
 * @license GNU Lesser General Public License v3 <http://www.opensource.org/licenses/lgpl-3.0.html>
 * @since   10.05.2010
 * @version SVN: $Id: ClientSession.java 125 2010-05-13 12:33:26Z christian-kerl@web.de $
 */
package org.cakis.server.service.session;

import java.io.Serializable;
import java.util.Date;

import javax.ejb.Remove;

import org.cakis.server.Names;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Create;
import org.jboss.seam.annotations.Destroy;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.core.Events;
import org.jboss.seam.log.Log;
import org.jboss.seam.web.Session;

/**
 * ClientSession provides methods to manage a session with a remote client.
 *
 * @author Christian Kerl
 */
@Name(Names.ClientSession)
@Scope(ScopeType.SESSION)
public class ClientSession implements Serializable {
    private static final long serialVersionUID = 1L;

    public static final String EventStarted = "org.cakis.server.service.ClientSession.started";
    public static final String EventUpdated = "org.cakis.server.service.ClientSession.updated";
    public static final String EventAddEvent = "org.cakis.server.service.ClientSession.addEvent";
    public static final String EventDestroyed = "org.cakis.server.service.ClientSession.destroyed";

    @Logger
    private Log log;

    @In
    private Events events;

    @In
    private Session session;

    private ClientSessionInfo info;

    public ClientSessionInfo getInfo() {
        return info;
    }

    public boolean isInited() {
        return getInfo().getState().equals(ClientSessionInfo.State.Inited);
    }

    @Create
    public void create() {
        if(getInfo() != null) {
            throw new IllegalStateException();
        }

        info = new ClientSessionInfo();
        info.setState(ClientSessionInfo.State.Started);

        events.raiseEvent(EventStarted, this.getInfo());

        log.info("#0 created", this);
    }

    public void init(String clientId) {
        checkSessionState(ClientSessionInfo.State.Started);

        getInfo().setClientId(clientId);
        getInfo().setState(ClientSessionInfo.State.Inited);

        log.info("#0 inited", this);

        access();
    }

    public void access() {
        checkSessionState(ClientSessionInfo.State.Inited);

        getInfo().setLastAccess(new Date());

        events.raiseEvent(EventUpdated, this.getInfo());

        log.info("#0 accessed", this);
    }

    public void event(ClientSessionRedirectEvent event) {
        checkSessionState(ClientSessionInfo.State.Inited);

        if(event == null || !getInfo().equals(event.getSession())) {
            throw new IllegalArgumentException();
        }

        events.raiseEvent(EventAddEvent, event);

        log.info("#0 added", event);
    }

    @Destroy @Remove
    public void destroy() {
        getInfo().setState(ClientSessionInfo.State.Invalid);

        session.invalidate();

        // HACK: this event should be raised when the component is destroyed by ServletLifecycle#endSession()
        // but this method can not be called, see also SOAPRequestHandler#close()
        //Events.instance().raiseEvent("org.jboss.seam.preDestroy.ClientSession", this);

        events.raiseEvent(EventDestroyed, this.getInfo());
    }

    @Override
    public String toString() {
        return "ClientSession[id=" + getInfo().getId() + ", client=" + getInfo().getClientId() + "]";
    }

    private void checkSessionState(ClientSessionInfo.State expected) {
        if(getInfo() == null || !expected.equals(getInfo().getState())) {
            throw new IllegalStateException();
        }
    }
}
